package server.net.io;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.logging.Level;
import java.util.logging.Logger;
import server.GameServer;
import server.net.ClientConnection;

/**
 *
 * This class handles new incoming connections. We create and listen on a
 * socket. Any new connections are encapsulated into a NimConnection object
 * then registered with the Dispatchers for further IO. Finally, we go back
 * to listening for new connections.
 * 
 * @author Vincent Tsuei
 */
public class Acceptor implements Runnable {
    
    private final int portNumber;
    private boolean shutdown = false;
    private GameServer server;
    private ServerSocketChannel socketChannel;
    
    public Acceptor(GameServer s, int port) {
        server = s;
        portNumber = port;
    }

    @Override
    public void run() {
        
        //Create a ServerSocketChannel. This is a single blocking socket since
        //handling only the client connection request should be fairly fast.
        try {
            socketChannel = ServerSocketChannel.open();
            socketChannel.socket().setReuseAddress(true);
            socketChannel.configureBlocking(true);
            socketChannel.socket().bind(new InetSocketAddress(portNumber));
        } catch (IOException ex) {
            Logger.getLogger(Acceptor.class.getName()).log(Level.SEVERE, "Could not bind ServerSocket. Exiting.", ex);
            System.exit(-1);
        }
        
        //ServerSocket bound
        Logger.getLogger(Acceptor.class.getName()).log(Level.INFO, "ServerSocket listening at port {0}. Ready to accept incoming connections.", new Object[] {Integer.toString(portNumber)});
        
        //Main accept() loop. Create a NimConnection object for each incoming 
        //connection. Then, register it to the dispatcher that has the least load.
        while (!server.isShuttingDown() && !shutdown) {
            try {
                SocketChannel newClient = socketChannel.accept();
                newClient.configureBlocking(false);
                Logger.getLogger(Acceptor.class.getName()).log(Level.INFO, "Accepted connection from {0}:{1}.", new Object[]{newClient.socket().getInetAddress(), Integer.toString(newClient.socket().getPort())});
                
                //Create a connection wrapper
                ClientConnection connection = new ClientConnection(server, newClient);
                
                //Get a dispatcher and register the connection with it.
                Dispatcher dispatch = server.getLeastLoadDispatcher();
                dispatch.register(connection);
                
            } catch (IOException ex) {
                Logger.getLogger(Acceptor.class.getName()).log(Level.SEVERE, "IOException in server accept() loop.", ex);
            } catch (Exception ex) {
                Logger.getLogger(Acceptor.class.getName()).log(Level.SEVERE, "Exception in server accept() loop.", ex);
            }
        }
    }
    
    //TODO.
    public void shutdown() {
        shutdown = true;
    }
}
